using System;
using System.Collections.Generic;
using System.Text;

namespace Common.Shared {
	/// <summary>
	/// Static class to convert strings to typed values
	/// </summary>
	public static class StringConverter {
		/// <summary>
		/// Converts the string.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <param name="convertedValue">The converted value.</param>
		/// <returns></returns>
		public static Type ConvertString ( string value, out object convertedValue ) {
			// First check the whole number types, because floating point types will always parse whole numbers
			// Start with the smallest types
			byte byteResult;
			if ( byte.TryParse ( value, out byteResult ) ) {
				convertedValue = byteResult;
				return typeof ( byte );
			}

			short shortResult;
			if ( short.TryParse ( value, out shortResult ) ) {
				convertedValue = shortResult;
				return typeof ( short );
			}

			int intResult;
			if ( int.TryParse ( value, out intResult ) ) {
				convertedValue = intResult;
				return typeof ( int );
			}

			long longResult;
			if ( long.TryParse ( value, out longResult ) ) {
				convertedValue = longResult;
				return typeof ( long );
			}

			ulong ulongResult;
			if ( ulong.TryParse ( value, out ulongResult ) ) {
				convertedValue = ulongResult;
				return typeof ( ulong );
			}

			// No need to check the rest of the unsigned types, which will fit into the signed whole number types

			// Next check the floating point types
			Single floatResult;
			if ( Single.TryParse ( value, out floatResult ) ) {
				convertedValue = floatResult;
				return typeof ( Single );
			}


			// It's not clear that there's anything that double.TryParse() and decimal.TryParse() will parse 
			// but which float.TryParse() won't
			double doubleResult;
			if ( double.TryParse ( value, out doubleResult ) ) {
				convertedValue = doubleResult;
				return typeof ( double );
			}

			decimal decimalResult;
			if ( decimal.TryParse ( value, out decimalResult ) ) {
				convertedValue = decimalResult;
				return typeof ( decimal );
			}

			// It's not a number, so it's either a bool, char or string
			bool boolResult;
			if ( bool.TryParse ( value, out boolResult ) ) {
				convertedValue = boolResult;
				return typeof ( bool );
			}

			char charResult;
			if ( char.TryParse ( value, out charResult ) ) {
				convertedValue = charResult;
				return typeof ( char );
			}

			convertedValue = value;
			return typeof ( string );
		}

		/// <summary>
		/// Compare two types and find a type that can fit both of them
		/// </summary>
		/// <param name="typeA">First type to compare</param>
		/// <param name="typeB">Second type to compare</param>
		/// <returns>The type that can fit both types, or string if they're incompatible</returns>
		public static Type FindCommonType ( Type typeA, Type typeB ) {
			// Build the singleton type map (which will rebuild it in a typesafe manner
			// if it's not already built).
			BuildTypeMap ( );

			if ( !typeMap.ContainsKey ( typeA ) )
				return typeof ( string );

			if ( !typeMap[ typeA ].ContainsKey ( typeB ) )
				return typeof ( string );

			return typeMap[ typeA ][ typeB ];
		}

		public static string FixedWidthString ( string s, int width ) {
			StringBuilder sb = new StringBuilder ( width );
			int sLen = s.Trim ( ).Length;
			if ( sLen < width ) {
				sb.Append ( s.Trim ( ) );
				int diff = width - sLen;
				for ( int i = 0; i < diff; i++ ) {
					sb.Append ( " " );
				}
			} else {
				sb.Append ( s.Trim ( ).Substring ( 0, width ) );
			}
			return sb.ToString ( );

		}


		// Dictionary to map two types to a common type that can hold both of them
		private static Dictionary<Type, Dictionary<Type, Type>> typeMap = null;

		// Locker object to build the singleton typeMap in a typesafe manner
		private static object locker = new object ( );

		/// <summary>
		/// Build the singleton type map in a typesafe manner.
		/// This map is a dictionary that maps a pair of types to a common type.
		/// So typeMap[typeof(float)][typeof(uint)] will return float, while
		/// typemap[typeof(char)][typeof(bool)] will return string.
		/// </summary>
		private static void BuildTypeMap ( ) {
			lock ( locker ) {
				if ( typeMap == null ) {
					typeMap = new Dictionary<Type, Dictionary<Type, Type>> ( );
					Dictionary<Type, Type> td = new Dictionary<Type, Type> ( );
					// Comparing byte
					td.Add ( typeof ( byte ), typeof ( byte ) );
					td.Add ( typeof ( short ), typeof ( short ) );
					td.Add ( typeof ( int ), typeof ( int ) );
					td.Add ( typeof ( long ), typeof ( long ) );
					td.Add ( typeof ( ulong ), typeof ( ulong ) );
					td.Add ( typeof ( float ), typeof ( float ) );
					td.Add ( typeof ( double ), typeof ( double ) );
					td.Add ( typeof ( decimal ), typeof ( decimal ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( byte ), td );

					//Comparing short
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( short ) );
					td.Add ( typeof ( short ), typeof ( short ) );
					td.Add ( typeof ( int ), typeof ( int ) );
					td.Add ( typeof ( long ), typeof ( long ) );
					td.Add ( typeof ( ulong ), typeof ( ulong ) );
					td.Add ( typeof ( float ), typeof ( float ) );
					td.Add ( typeof ( double ), typeof ( double ) );
					td.Add ( typeof ( decimal ), typeof ( decimal ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( short ), td );

					//Comparing int
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( int ) );
					td.Add ( typeof ( short ), typeof ( int ) );
					td.Add ( typeof ( int ), typeof ( int ) );
					td.Add ( typeof ( long ), typeof ( long ) );
					td.Add ( typeof ( ulong ), typeof ( ulong ) );
					td.Add ( typeof ( float ), typeof ( float ) );
					td.Add ( typeof ( double ), typeof ( double ) );
					td.Add ( typeof ( decimal ), typeof ( decimal ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( int ), td );

					//Comparing long
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( long ) );
					td.Add ( typeof ( short ), typeof ( long ) );
					td.Add ( typeof ( int ), typeof ( long ) );
					td.Add ( typeof ( long ), typeof ( long ) );
					td.Add ( typeof ( ulong ), typeof ( ulong ) );
					td.Add ( typeof ( float ), typeof ( float ) );
					td.Add ( typeof ( double ), typeof ( double ) );
					td.Add ( typeof ( decimal ), typeof ( decimal ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( long ), td );

					//Comparing ulong
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( ulong ) );
					td.Add ( typeof ( short ), typeof ( ulong ) );
					td.Add ( typeof ( int ), typeof ( ulong ) );
					td.Add ( typeof ( long ), typeof ( ulong ) );
					td.Add ( typeof ( ulong ), typeof ( ulong ) );
					td.Add ( typeof ( float ), typeof ( float ) );
					td.Add ( typeof ( double ), typeof ( double ) );
					td.Add ( typeof ( decimal ), typeof ( decimal ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( ulong ), td );

					//Comparing float
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( float ) );
					td.Add ( typeof ( short ), typeof ( float ) );
					td.Add ( typeof ( int ), typeof ( float ) );
					td.Add ( typeof ( long ), typeof ( float ) );
					td.Add ( typeof ( ulong ), typeof ( float ) );
					td.Add ( typeof ( float ), typeof ( float ) );
					td.Add ( typeof ( double ), typeof ( double ) );
					td.Add ( typeof ( decimal ), typeof ( decimal ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( float ), td );

					//Comparing double
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( double ) );
					td.Add ( typeof ( short ), typeof ( double ) );
					td.Add ( typeof ( int ), typeof ( double ) );
					td.Add ( typeof ( long ), typeof ( double ) );
					td.Add ( typeof ( ulong ), typeof ( double ) );
					td.Add ( typeof ( float ), typeof ( double ) );
					td.Add ( typeof ( double ), typeof ( double ) );
					td.Add ( typeof ( decimal ), typeof ( decimal ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( double ), td );

					//Comparing decimal
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( decimal ) );
					td.Add ( typeof ( short ), typeof ( decimal ) );
					td.Add ( typeof ( int ), typeof ( decimal ) );
					td.Add ( typeof ( long ), typeof ( decimal ) );
					td.Add ( typeof ( ulong ), typeof ( decimal ) );
					td.Add ( typeof ( float ), typeof ( decimal ) );
					td.Add ( typeof ( double ), typeof ( decimal ) );
					td.Add ( typeof ( decimal ), typeof ( decimal ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( decimal ), td );

					//Comparing decimal
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( string ) );
					td.Add ( typeof ( short ), typeof ( string ) );
					td.Add ( typeof ( int ), typeof ( string ) );
					td.Add ( typeof ( long ), typeof ( string ) );
					td.Add ( typeof ( ulong ), typeof ( string ) );
					td.Add ( typeof ( float ), typeof ( string ) );
					td.Add ( typeof ( double ), typeof ( string ) );
					td.Add ( typeof ( decimal ), typeof ( string ) );
					td.Add ( typeof ( bool ), typeof ( bool ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( bool ), td );

					//Comparing char
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( string ) );
					td.Add ( typeof ( short ), typeof ( string ) );
					td.Add ( typeof ( int ), typeof ( string ) );
					td.Add ( typeof ( long ), typeof ( string ) );
					td.Add ( typeof ( ulong ), typeof ( string ) );
					td.Add ( typeof ( float ), typeof ( string ) );
					td.Add ( typeof ( double ), typeof ( string ) );
					td.Add ( typeof ( decimal ), typeof ( string ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( char ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( char ), td );

					//Comparing char
					td = new Dictionary<Type, Type> ( );
					td.Add ( typeof ( byte ), typeof ( string ) );
					td.Add ( typeof ( short ), typeof ( string ) );
					td.Add ( typeof ( int ), typeof ( string ) );
					td.Add ( typeof ( long ), typeof ( string ) );
					td.Add ( typeof ( ulong ), typeof ( string ) );
					td.Add ( typeof ( float ), typeof ( string ) );
					td.Add ( typeof ( double ), typeof ( string ) );
					td.Add ( typeof ( decimal ), typeof ( string ) );
					td.Add ( typeof ( bool ), typeof ( string ) );
					td.Add ( typeof ( char ), typeof ( string ) );
					td.Add ( typeof ( string ), typeof ( string ) );
					typeMap.Add ( typeof ( string ), td );

				}
			}
		}

		/// <summary>
		/// Converts the specified string to the defined enum type.
		/// </summary>
		/// <typeparam name="T">The enum type</typeparam>
		/// <param name="value">The value.</param>
		/// <returns></returns>
		public static T ToEnum<T> ( string value ) {
			try {
				return (T)Enum.Parse ( typeof ( T ), value, true );
			} catch {
				throw;
			}
		}
	}
}
